package com.sunwayworld.baseline.framework.deviceDocking.boneequipt.service;

import com.sunwayworld.baseline.framework.deviceDocking.boneequipt.dto.BoneResultDto;
import com.sunwayworld.baseline.framework.deviceDocking.constant.InstrumentNameConstants;
import com.sunwayworld.baseline.framework.deviceDocking.handler.DockingObserver;
import com.sunwayworld.basemodule.business.result.bean.LimsResultBean;
import com.sunwayworld.basemodule.business.result.computing.OrderTaskResultComputingEngine;
import com.sunwayworld.basemodule.business.result.service.LimsResultService;
import com.sunwayworld.framework.context.ApplicationContextHelper;
import com.sunwayworld.framework.executor.ExecutorHelper;
import com.sunwayworld.framework.log.annotation.LogType;
import com.sunwayworld.framework.mybatis.mapper.MatchPattern;
import com.sunwayworld.framework.mybatis.mapper.SearchFilter;
import com.sunwayworld.framework.utils.CollectionUtils;
import com.sunwayworld.framework.utils.StringUtils;
import com.sunwayworld.module.sys.log.bean.CoreLogBean;
import com.sunwayworld.module.sys.log.service.CoreLogService;
import io.netty.channel.ChannelHandlerContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 消息处理类 -- 观察者。
 * 骨密度仪
 */
@Component
public class BoneEquiptMessageObserver implements DockingObserver {

    private static final Logger log = LogManager.getLogger(BoneEquiptMessageObserver.class);

    // 这两是结尾符
    private static final String CR = String.valueOf((char)13);  //<CR>代表回车码 0D  ^M
    private static final String LF = String.valueOf((char)10);  //<LF>换行 0A
    // 多数据换行符，二进制为：03
    private static final String ETX = String.valueOf((char)3);  // 正文结束  ^C
    // 行数据分割
    private static final String LINE_SPLIT = LF;
    // 多记录分割
    private static final String RESULT_SPLIT = ETX;
    private static final AtomicReference<String> result = new AtomicReference<>(); // 接收的数据

    // 骨密度仪结果指定检测项目编码：
    public static final String boneEquiptProductCode = "P00024";
    // 骨密度仪结果指定检测项目名称，日志打印用
    public static final String boneEquiptProductName = "骨质疏松检测 二项";
    // 指定分析项名称
    public static final String boneAnalyte1 = "CROSSL";
    public static final String boneAnalyte2 = "P1NP";

    @Lazy
    @Autowired
    private CoreLogService logService;

    @Autowired
    private LimsResultService resultService;

    @Override
    public String getType() {
        return InstrumentNameConstants.TG_XY;
    }

    @Override
    public void handle(ChannelHandlerContext ctx, String data) {
        log.info("接收数据：" + data);
        // 接收数据直接写入数据库保存
        insertLog(data);
        if(checkData(data)) { // 如果接收到完整的数据直接处理数据，并清空缓存。
            this.handleMessage(data, ctx);
            result.set(null);
        } else if (StringUtils.isEmpty(result.get())){
            result.set(data);
        } else {
            result.getAndUpdate(v -> v + data);
            if(checkData(result.get())){  // 多条数据组装
                this.handleMessage(result.get(), ctx);
                result.set(null);
            }
        }
    }

    // 判断数据是否包含开始和结束，HL7数据是否完整
    private static boolean checkData(String data){
        return true;
        // 多条数据同发，用二进制03分割
//        List<String> split = Arrays.asList(data.split(RESULT_SPLIT));
//        boolean start = false;
//        boolean end = false;
//        for (String s : split) {
//            if(s.startsWith("H|")) start = true;
//            if(s.startsWith("L|")) end = true;
//        }
//        return start && end;
    }

    private void handleMessage(String message, ChannelHandlerContext ctx) {
        log.info("接收消息：" + message);
        // 按行分割
        List<String> split = Arrays.asList(message.split(LINE_SPLIT));
        // 解析到分析项结果
        List<BoneResultDto> resultDTOS = new ArrayList<>();
        String hissampleCode = null;
        for(String s : split) {
            String[] info = s.split("\\|");
            String title = info[0].substring(info[0].length() -1);
            switch (title){
                case "H" :
                    break;
                case "O" :
                    // 示例：O|1|526^0^0|@202404220007|232^CROSSL^0^1\236^P1NP^0^1|N||||||||||0||||||||||F|||||
                    // 0-3：第三个域为分院样本编号
                    hissampleCode = info[3];
                    break;
                case "R" :
                    // 示例：R|1|232^CROSSL^1^F|0.358999997377396|IU/mL|100^100|||F|0||20240422165942|20240423192406|UD90^123456
                    // R-2: 编码-分析项名称-重复次数-定性定量
                    // R-3: 分析项值
                    // R-6 异常标记，> < 空
                    String[] testInfo = info[2].split("\\^");
                    BoneResultDto boneResultDto = new BoneResultDto();
                    boneResultDto.setHissampleCode(hissampleCode);
                    boneResultDto.setAnalyte(testInfo[1]);
                    // 异常标记，可能的值为：>,<或空，非空时，将该值拼接到结果值的前边
                    if (!StringUtils.isEmpty(info[6])) {
                        boneResultDto.setResult(info[6] + info[3].split("\\^")[0]);
                    } else {
                        boneResultDto.setResult(info[3].split("\\^")[0]);
                    }
                    // 检测项目，指定
                    boneResultDto.setProductCode(boneEquiptProductCode);
                    boneResultDto.setProductName(boneEquiptProductName);
                    resultDTOS.add(boneResultDto);
                    break;
                default:
            }
        }
        if(!CollectionUtils.isEmpty(resultDTOS)){
            resultDTOS.forEach(u->{
                log.info("解析分析结果：分院样本编号：{}， 分析项名：{}， 分析项值：{}，检测项目：{}",
                    u.getHissampleCode(), u.getAnalyte(), u.getResult(), u.getProductName());
            });
            // 指定检测项目：P00024-骨质疏松检测 二项
            String productCode = ApplicationContextHelper.getConstantValue("BONE_EQUIPT_PRODUCTCODE", boneEquiptProductCode);
            // 获取当前处于实验结果录入阶段，且检测项目为P00024的，指定分析项结果录入的数据
            // 指定分析项：CROSSL，P1NP
            List<String> analytes = new ArrayList<>();
            analytes.add(boneAnalyte1);
            analytes.add(boneAnalyte2);
            // 获取处于结果录入节点单的指定检测项目指定分析项的分析项结果数据
            List<LimsResultBean> oriResults = resultService.selectListByFilter(SearchFilter.instance()
                .match("PRODUCTCODE", productCode).filter(MatchPattern.SEQ)// 指定检测项目
                .match("ANALYTE", analytes).filter(MatchPattern.OR)// 指定分析项
                .match("RUNSTATUS", "logged").filter(MatchPattern.SEQ)// 实验批次处于结果录入
                .match("OTSTATUS", "logged").filter(MatchPattern.SB)// 工序任务处于结果录入
                .match("OTFALG", "1").filter(MatchPattern.SEQ));// 工序任务的标记为1;
            if (CollectionUtils.isEmpty(oriResults)) {
                return;
            }
            // 更新结果到lims
            List<LimsResultBean> updateResults = new ArrayList<>();
            // 匹配并导入结果，按检测项目编码-分析项名-分院样本编号来匹配结果
            for (LimsResultBean oriResult: oriResults) {
                if (!StringUtils.isEmpty(oriResult.getExt$Item("HISSAMPLECODE"))) {
                    // 按分院样本编号-分析项名匹配即可，检测项目已指定
                    Optional<BoneResultDto> resultOp = resultDTOS.stream().filter(t->
                        StringUtils.equalsIgnoreCase(t.getAnalyte(), oriResult.getAnalyte())
                        && StringUtils.equalsIgnoreCase(t.getHissampleCode(), oriResult.getExt$Item("HISSAMPLECODE"))
                    ).findFirst();
                    if (resultOp.isPresent()) {
                        oriResult.setOriginalResult(resultOp.get().getResult());
                        oriResult.setFinalResult(resultOp.get().getResult());
                        updateResults.add(oriResult);
                    }
                }
            }
            if (CollectionUtils.isEmpty(updateResults)) {
                return;
            }
            resultService.getDao().fastUpdate(updateResults, "ORIGINALRESULT", "FINALRESULT");
            // 计算公式
            List<Long> folderIdList = updateResults.stream().map(LimsResultBean::getFolderId).distinct().collect(Collectors.toList());
            OrderTaskResultComputingEngine.getEngine(folderIdList).compute();
            ExecutorHelper.submit(() -> OrderTaskResultComputingEngine.getEngine(folderIdList).compute());
        }
    }

    private void insertLog(String content) {
        CoreLogBean logBean = new CoreLogBean();
        logBean.setId(ApplicationContextHelper.getNextIdentity());
        logBean.setUserId("AUTO");
        logBean.setModule("仪器对接");
        logBean.setName("骨密度仪");
        logBean.setContent(content);
        logBean.setType(LogType.DEFAULT.name());
        logBean.setStartTime(LocalDateTime.now());
        logService.insertLog(Arrays.asList(logBean));
    }

    public static void main(String[] args) {
        String message = "H|\\^&|0||unidiaginc-UD90^0.0.1^123456|||||||PR|1394-97|20240423192406" + LINE_SPLIT +
                "P|1||||||00010101|2||||||||||||||||||||||||||" + LINE_SPLIT +
                "O|1|529^0^0|@202404300002|232^CROSSL^0^1\\236^P1NP^0^1|N||||||||||0||||||||||F|||||" + LINE_SPLIT +
                "R|1|232^CROSSL^1^F|0.301999986171722|IU/mL|100^100|<||F|0||20240430184116|20240423192406|UD90^123456" + LINE_SPLIT +
                "R|1|236^P1NP^1^F|38.7200012207031|IU/mL|100^100|>||F|0||20240430184158|20240423192406|UD90^123456" + LINE_SPLIT +
                "L|0|N" + LINE_SPLIT +
                ETX +
                "H|\\^&|0||unidiaginc-UD90^0.0.1^123456|||||||PR|1394-97|20240423192406" + LINE_SPLIT +
                "P|1||||||00010101|2||||||||||||||||||||||||||" + LINE_SPLIT +
                "O|1|525^0^0|@202404220006|232^CROSSL^0^1\\236^P1NP^0^1|N||||||||||0||||||||||F|||||" + LINE_SPLIT +
                "R|1|232^CROSSL^1^F|0.025000000372529|IU/mL|100^100|<||F|0||20240422165818|20240423192406|UD90^123456" + LINE_SPLIT +
                "R|1|236^P1NP^1^F|16.7240009307861|IU/mL|100^100|>||F|0||20240422165900|20240423192406|UD90^123456" + LINE_SPLIT +
                "L|0|N" + LINE_SPLIT +
                ETX;
        // 按行分割
        List<String> split = Arrays.asList(message.split(LINE_SPLIT));
        // 解析到分析项结果
        List<BoneResultDto> resultDTOS = new ArrayList<>();
        String hissampleCode = null;
        for(String s : split) {
            String[] info = s.split("\\|");
            String title = info[0].substring(info[0].length() -1);
            switch (title){
                case "H" :
                    break;
                case "O" :
                    // 示例：O|1|526^0^0|@202404220007|232^CROSSL^0^1\236^P1NP^0^1|N||||||||||0||||||||||F|||||
                    // 0-3：第三个域为分院样本编号
                    hissampleCode = info[3];
                    break;
                case "R" :
                    // 示例：R|1|232^CROSSL^1^F|0.358999997377396|IU/mL|100^100|||F|0||20240422165942|20240423192406|UD90^123456
                    // R-2: 编码-分析项名称-重复次数-定性定量
                    // R-3: 分析项值
                    String[] testInfo = info[2].split("\\^");
                    BoneResultDto boneResultDto = new BoneResultDto();
                    boneResultDto.setHissampleCode(hissampleCode);
                    boneResultDto.setAnalyte(testInfo[1]);
                    // 异常标记，可能的值为：>,<或空，非空时，将该值拼接到结果值的前边
                    if (!StringUtils.isEmpty(info[6])) {
                        boneResultDto.setResult(info[6] + info[3].split("\\^")[0]);
                    } else {
                        boneResultDto.setResult(info[3].split("\\^")[0]);
                    }
                    // 检测项目，指定
                    boneResultDto.setProductCode(boneEquiptProductCode);
                    boneResultDto.setProductName(boneEquiptProductName);
                    resultDTOS.add(boneResultDto);
                    break;
                default:
            }
        }
        if(!CollectionUtils.isEmpty(resultDTOS)){
            resultDTOS.forEach(u->{
                log.info("解析分析结果：分院样本编号：{}， 分析项名：{}， 分析项值：{}，检测项目：{}",
                    u.getHissampleCode(), u.getAnalyte(), u.getResult(), u.getProductName());
            });
        }
    }

}
